﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
#if !NET40
using System.Threading.Tasks;
#endif
namespace WeShare.WeiXin.Common
{
    public class XmlUtil
    {
        public static void ToObject<T>(T obj, XElement doc,string ignoreDeclaring="")
        {
            var type = obj.GetType();
            var key = $"model_{type.FullName}";
            var propertys = CacheService.Get<List<EntityProperty>>(key) ?? new List<EntityProperty>();
            if (!propertys.Any())
            {
                foreach (var info in type.GetProperties())
                {
                    if (string.IsNullOrEmpty(ignoreDeclaring)==false&& info.DeclaringType != null && info.DeclaringType.Name == ignoreDeclaring)
                    {
                        continue;
                    }
                    var ca = info.GetCustomAttributes(false);
                    var temp = new EntityProperty
                    {
                        PropertyInfo = info,
                        PropertyName = info.Name,
                        FullName = info.PropertyType.FullName
                    };
                    if (ca != null && ca.Length > 0)
                    {
                        var attrs = ca[0] as WxXmlAttribute;
                        temp.HasChild = attrs.HasChild;
                        temp.IsList = attrs.IsList;
                    }
                    var ptype = Nullable.GetUnderlyingType(info.PropertyType);
                    if (ptype == null)
                    {
                        ptype = info.PropertyType;
                    }
                    temp.PropertyType = ptype;
                    propertys.Add(temp);
                }
                CacheService.Add(key, propertys);
            }
            foreach (var item in propertys)
            {
                var temlxml = doc.Elements(item.PropertyName);
                if (temlxml != null)
                {
                    if (!temlxml.Any())
                    {
                        continue;
                    }
                    if (item.HasChild && temlxml.Any())
                    {
                        //获取子属性值
                        var childobj = item.PropertyType.Assembly.CreateInstance(item.FullName);
                        ToObject(childobj, temlxml.First());
                        item.PropertyInfo.SetValue(obj, childobj, null);
                        continue;
                    }
                    if (item.IsList && temlxml.Any())
                    {
                        var templist = item.PropertyType.Assembly.CreateInstance(item.FullName);
                        var childtype = item.PropertyType.GetMethod("get_Item").ReturnType;
                        foreach (var xml in temlxml)
                        {
                            var tempchild = childtype.Assembly.CreateInstance(childtype.FullName);
                            ToObject(tempchild, xml);
                            item.PropertyType.GetMethod("Add").Invoke(templist, new object[] { tempchild });
                        }
                        item.PropertyInfo.SetValue(obj, templist, null);
                        continue;
                    }
                }
                item.PropertyInfo.SetValue(obj, Convert.ChangeType(temlxml.First().Value, item.PropertyType), null);
            }
        }
        public static T ToObject<T>(XElement doc)
        {
            var obj = Activator.CreateInstance<T>();
            ToObject(obj, doc);
            return obj;
        }
        public static Dictionary<string, string> ToDictionary(string xml)
        {
            var doc = XElement.Parse(xml);
            return doc.Elements().ToDictionary(element => element.Name.ToString(), element => element.Value);
        }
#if !NET40
        public static async Task<Dictionary<string, string>> ToDictionaryAsync(string xml)
        {
            return await Task.Run(() =>
            {
                var doc = XElement.Parse(xml);
                return doc.Elements().ToDictionary(element => element.Name.ToString(), element => element.Value);
            });

        }
        public static async Task<T> ToObjectAsync<T>(XElement doc)
        {
            var obj = Activator.CreateInstance<T>();
            await ToObjectAsync(obj, doc);
            return obj;
        }
        public static async Task ToObjectAsync<T>(T obj, XElement doc, string ignoreDeclaring = "")
        {
            await Task.Run(async () =>
           {
               var type = obj.GetType();
               var key = $"model_{type.FullName}";
               var propertys = CacheService.Get<List<EntityProperty>>(key) ?? new List<EntityProperty>();
               if (!propertys.Any())
               {
                   foreach (var info in type.GetProperties())
                   {
                       if (string.IsNullOrEmpty(ignoreDeclaring) == false && info.DeclaringType != null && info.DeclaringType.Name == ignoreDeclaring)
                       {
                           continue;
                       }
                       var ca = info.GetCustomAttributes(false);
                       var temp = new EntityProperty
                       {
                           PropertyInfo = info,
                           PropertyName = info.Name,
                           FullName = info.PropertyType.FullName
                       };
                       if (ca != null && ca.Length > 0)
                       {
                           var attrs = ca[0] as WxXmlAttribute;
                           temp.HasChild = attrs.HasChild;
                           temp.IsList = attrs.IsList;
                       }
                       var ptype = Nullable.GetUnderlyingType(info.PropertyType);
                       if (ptype == null)
                       {
                           ptype = info.PropertyType;
                       }
                       temp.PropertyType = ptype;
                       propertys.Add(temp);
                   }
                   CacheService.Add(key, propertys);
               }
               foreach (var item in propertys)
               {
                   var temlxml = doc.Elements(item.PropertyName);
                   if (temlxml != null)
                   {
                       if (!temlxml.Any())
                       {
                           continue;
                       }
                       if (item.HasChild && temlxml.Any())
                       {
                           //获取子属性值
                           var childobj = item.PropertyType.Assembly.CreateInstance(item.FullName);
                           await ToObjectAsync(childobj, temlxml.First());
                           item.PropertyInfo.SetValue(obj, childobj, null);
                           continue;
                       }
                       if (item.IsList && temlxml.Any())
                       {
                           var templist = item.PropertyType.Assembly.CreateInstance(item.FullName);
                           var childtype = item.PropertyType.GetMethod("get_Item").ReturnType;
                           foreach (var xml in temlxml)
                           {
                               var tempchild = childtype.Assembly.CreateInstance(childtype.FullName);
                               await ToObjectAsync(tempchild, xml);
                               item.PropertyType.GetMethod("Add").Invoke(templist, new object[] { tempchild });
                           }
                           item.PropertyInfo.SetValue(obj, templist, null);
                           continue;
                       }
                   }
                   item.PropertyInfo.SetValue(obj, Convert.ChangeType(temlxml.First().Value, item.PropertyType), null);
               }
           });
        }
#endif
    }
}
